﻿using AutoMapper;
using ChineseAreaService;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Mvc;
using Microsoft.Data.Entity;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Caching.Redis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.PlatformAbstractions;
using Newtonsoft.Json;
using Sino.BankInfo;
using Sino.CapacityCloud.Infrastructure.Events;
using Sino.CapacityCloud.Infrastructure.Filters;
using Sino.CapacityCloud.Infrastructure.Midwares;
using Sino.CapacityCloud.Infrastructure.Utilities;
using Sino.CapacityCloud.Repositories.Models;
using Sino.CapacityCloud.Services;
using Sino.CapacityCloud.WebApi.Utilities;
using Sino.WebApi.Framework.Abstractions.Cryptography;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;

namespace Sino.CapacityCloud.Web
{

    public class Startup
    {

        public IPlatform Platform { get; private set; }

        public IConfigurationRoot Configuration { get; private set; }

        public IHostingEnvironment HostingEnvironment { get; private set; }

        public IApplicationEnvironment ApplicationEnvironment { get; private set; }

        public bool IsRunningOnNonMicrosoftPlatform
        {
            get
            {
                return !Platform.IsRunningOnWindows
                   || Platform.IsRunningOnMono
                   || Platform.IsRunningOnNanoServer;
            }
        }

        public Startup(IApplicationEnvironment applicationEnvironment, IHostingEnvironment env, IRuntimeEnvironment runtimeEnvironment)
        {
            Console.OutputEncoding = System.Text.Encoding.GetEncoding("gb18030");

            JsonConvert.DefaultSettings = () => new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Serialize };

            this.Platform = new Platform(runtimeEnvironment, env);

            this.ApplicationEnvironment = applicationEnvironment;
            this.HostingEnvironment = env;

            // Set up configuration sources.
            var builder = new ConfigurationBuilder()
              .SetBasePath(applicationEnvironment.ApplicationBasePath)
              .AddJsonFile("appsettings.json")
              .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
              .AddJsonFile("secrets.json", optional: true)
              //All environment variables in the process's context flow in as configuration values.
              .AddEnvironmentVariables();

            //if (env.IsDevelopment())
            //{
            //    // For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
            //    builder.AddUserSecrets();

            //    // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
            //    builder.AddApplicationInsightsSettings(developerMode: true);
            //}

            Mapper.Initialize(config =>
            {
                config.AddProfile<AutoMapperProfile>();
            });

            //builder.AddEnvironmentVariables();
            this.Configuration = builder.Build();
        }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            //services.AddApplicationInsightsTelemetry(Configuration);

            var runningOnNonMicrosoftPlatform = !Platform.IsRunningOnWindows
               || Platform.IsRunningOnMono
               || Platform.IsRunningOnNanoServer;

            var dataSection = Configuration.GetSection("Data");
            var connectionStrings = dataSection.GetChildren();
            var activeConnectionString = connectionStrings.FirstOrDefault(x => x.Get<bool>("IsActive"));

            if (runningOnNonMicrosoftPlatform)
            {
                services.AddEntityFramework()
                    .AddNpgsql()
                    .AddDbContext<ApplicationDbContext>(options =>
                        options.UseNpgsql(dataSection["PostgreSqlConnection:ConnectionString"]));
            }
            else
            {
                try
                {
                    switch (activeConnectionString["Provider"])
                    {
                        case "Sqlite":
                            services.AddEntityFramework()
                                .AddSqlite()
                                .AddDbContext<ApplicationDbContext>(options =>
                                    options.UseSqlite(activeConnectionString["ConnectionString"]));
                            break;
                        case "SqlServer":
                        case "LocalDb":
                        case "LocalDbIIS":
                            if (runningOnNonMicrosoftPlatform)
                            {
                                activeConnectionString = connectionStrings.FirstOrDefault(x => x.Get<string>("Provider") == "Sqlite");
                                goto case "Sqlite";
                            }
                            else
                            {
                                services.AddEntityFramework()
                                    .AddSqlServer()
                                    .AddDbContext<ApplicationDbContext>(options =>
                                        options.UseSqlServer(activeConnectionString["ConnectionString"]));
                            }
                            break;
                        default:
                            services.AddEntityFramework()
                                .AddInMemoryDatabase()
                                .AddDbContext<ApplicationDbContext>(options =>
                                {
                                    options.UseInMemoryDatabase();
                                });
                            break;
                    }
                }
                catch (Exception ex)
                {
#if DEBUG
                    Debug.WriteLine(ex.Message + Environment.NewLine + ex.StackTrace);
                    if (Debugger.IsAttached)
                    {
                        Debugger.Break();
                    }
#endif
                    services.AddEntityFramework()
                        .AddInMemoryDatabase()
                        .AddDbContext<ApplicationDbContext>(options =>
                        {
                            options.UseInMemoryDatabase();
                        });
                }
            }

            services.AddOptions();

            services.AddIdentity<ApplicationUser, ApplicationRole>()
               .AddEntityFrameworkStores<ApplicationDbContext>()
               .AddUserManager<ApplicationUserManager>()
               .AddRoleManager<ApplicationRoleManager>()
               .AddDefaultTokenProviders();

            services.AddScoped<SignInManager<ApplicationUser>, ApplicationSignInManager>();
            services.AddScoped<ApplicationSignInManager, ApplicationSignInManager>();
            services.AddScoped<CarManager, CarManager>();
            services.AddScoped<TransportLinkageManager, TransportLinkageManager>();

            if (this.Platform.IsRunningOnMono)
            {
                services.AddCaching();
            }
            else
            {
                //services.AddRedisCache();
                services.AddSingleton<IDistributedCache>(serviceProvider =>
                {
                    var cache = new RedisCache(new RedisCacheOptions()
                    {
                        Configuration = this.Configuration["Data:DefaultConnection:RedisConnection:Configuration"],
                        InstanceName = this.Configuration["Data:DefaultConnection:RedisConnection:InstanceName"]
                    });
                    while (true)
                    {
                        try
                        {
                            cache.Connect();
                            break;
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine(ex.Message + Environment.NewLine + ex.StackTrace);
                            Thread.Sleep(200);
                        }
                    }
                    return cache;
                });
            }

            services.AddCors(options =>
            {
                options.AddPolicy("CorsPolicy", builder =>
                {
                    builder.WithOrigins("*");
                });
            });

            services.AddMvc((options) =>
            {
                options.Filters.Add(typeof(GlobalExceptionFilter));

                options.CacheProfiles.Add("Default",
                    new CacheProfile()
                    {
                        Duration = 60,
                    });
                options.CacheProfiles.Add("Never",
                    new CacheProfile()
                    {
                        Location = ResponseCacheLocation.None,
                        NoStore = true
                    });
            });

            services.Configure<IdentityOptions>(options =>
            {
                options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(10);
                options.Lockout.MaxFailedAccessAttempts = 10;
            });

            services.AddBackgroundWorker<IEventHandlers, EventHandlers>();
            //services.AddBackgroundWorker<ITmsSyncOrderBackgroundWorker, TmsSyncOrderBackgroundWorker>();

            //if (this.Configuration.GetSection("Data:DefaultConnection:OldTms").Get<bool>("KeepTracking", false))
            //{
            //    services.AddBackgroundWorker<IOldCcpDataSyncWorker, OldCcpDataSyncWorker>();
            //}

            this.ConfigureApplicationServices(services);
        }

        protected virtual void ConfigureApplicationServices(IServiceCollection services)
        {
            // Add application services.
            services.AddInstance<IPlatform>(this.Platform);

            services.AddTransient<ISerializer, Services.JsonSerializer>();
            services.AddTransient<ITokenGenerator, TokenGenerator>();
            services.AddTransient<IRandomPasswordService, RandomPasswordService>();
            services.AddTransient<ITokenGenertationService, TokenGenertationService>();
            services.AddTransient<IEmailSender, EmailSender>();

            //if (this.Platform.IsDevelopment)
            //{
            //    services.AddTransient<ISmsSender, FakeSmsSender>();
            //}
            //else
            //{
            services.AddTransient<ISmsSender, SmsSender>();
            //}

            services.AddTransient<ICaptchasGenerator, CaptchasGenerator>();
            services.AddTransient<ICaptchasService, CaptchasService>();
            services.AddTransient<IBankCardInfoService, BankCardInfoService>();
            services.AddTransient<IBankCardService, BankCardService>();
            services.AddTransient<IChinaAreaService, ChinaAreaService>();

            services.AddSingleton<IChattingService, ChattingService>();
            services.AddSingleton<IAzureStorageService, AzureStorageService>();

            services.AddEventBus<SimpleEventBus>();

            var azureEventBusConfig = this.Configuration.GetSection("Data:DefaultConnection:AzureEventBus");

            services.AddSingleton<ITmsServiceBus, TmsServiceBus>();
            services.AddSingleton<IServiceBusRepeater, ServiceBusRepeater>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public virtual void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            var logger = loggerFactory.CreateLogger<Startup>();

            //app.UseApplicationInsightsExceptionTelemetry();
            //app.UseApplicationInsightsRequestTelemetry();

            if (env.IsDevelopment())
            {
                app.UseBrowserLink();
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
            }
            else
            {
                // Add Error handling middleware which catches all application specific errors and
                // send the request to the following path or controller action.
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear());

            app.UseDefaultFiles();
            app.UseStaticFiles(new Dictionary<string, string>()
            {
                { ".less", "text/css" },
                { ".map", "text/css" }
            });

            app.UseStartTimeHeader();

            app.UseIdentity();

            app.UseRequestLoggerMiddleware();
            app.UseRequestApiCompatibilityVerificationMiddleware();
            //app.UseRequestApiCompatibilityVerificationMiddleware(ApiController.Version);

            // To configure external authentication please see http://go.microsoft.com/fwlink/?LinkID=532715

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");

                routes.MapRoute(
                   name: "api",
                   template: "api/{controller}/{action}/{id?}");
            });

            try
            {
                ApplicationDbInitializer.Seed(app.ApplicationServices);
            }
            catch (Exception ex)
            {
                logger.LogCritical(ex.Message, ex);
            }

            //app.ApplicationServices.GetService<ITmsSyncOrderBackgroundWorker>();
            app.ApplicationServices.GetService<IEventHandlers>();

            //if (this.Configuration.GetSection("Data:DefaultConnection:OldTms").Get<bool>("KeepTracking", false))
            //{
            //    app.ApplicationServices.GetService<IOldCcpDataSyncWorker>();
            //}
        }

        // Entry point for the application.
        public static void Main(string[] args)
        {
            JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
            {
                ReferenceLoopHandling = ReferenceLoopHandling.Serialize
            };
            WebApplication.Run<Startup>(args);
        }

    }

}